use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use super::{bar::NewBar, enums::Direction, fx::FX};

/// 笔对象
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BI {
    /// 标的代码
    pub symbol: String,
    /// 笔开始的分型
    pub fx_a: FX,
    /// 笔结束的分型
    pub fx_b: FX,
    /// 笔内部的分型列表
    pub fxs: Vec<FX>,
    /// 方向
    pub direction: Direction,
    /// 笔内部的K线
    pub bars: Vec<NewBar>,
    /// 缓存，用户缓存
    pub cache: HashMap<String, serde_json::Value>,
}

impl BI {
    /// 创建新的笔
    pub fn new(
        symbol: String,
        fx_a: FX,
        fx_b: FX,
        fxs: Vec<FX>,
        direction: Direction,
        bars: Vec<NewBar>,
    ) -> Self {
        Self {
            symbol,
            fx_a: fx_a.clone(),
            fx_b: fx_b.clone(),
            fxs,
            direction,
            bars,
            cache: HashMap::new(),
        }
    }

    /// 开始时间
    pub fn sdt(&self) -> DateTime<Utc> {
        self.fx_a.dt
    }

    /// 结束时间
    pub fn edt(&self) -> DateTime<Utc> {
        self.fx_b.dt
    }

    /// 最高价
    pub fn high(&self) -> f64 {
        self.fx_a.high.max(self.fx_b.high)
    }

    /// 最低价
    pub fn low(&self) -> f64 {
        self.fx_a.low.min(self.fx_b.low)
    }

    /// 价差力度
    pub fn power_price(&self) -> f64 {
        (self.fx_b.fx - self.fx_a.fx).abs()
    }

    /// 成交量力度
    pub fn power_volume(&self) -> f64 {
        if self.bars.len() < 2 {
            return 0.0;
        }
        self.bars[1..self.bars.len()-1].iter().map(|x| x.vol).sum()
    }

    /// 笔的涨跌幅
    pub fn change(&self) -> f64 {
        (self.fx_b.fx - self.fx_a.fx) / self.fx_a.fx
    }

    /// 笔的无包含关系K线数量
    pub fn length(&self) -> usize {
        self.bars.len()
    }

    /// 笔内部的信噪比
    pub fn snr(&self) -> f64 {
        let bars = self.raw_bars();
        if bars.is_empty() {
            return 0.0;
        }
        
        let total_change = (bars.last().unwrap().close - bars.first().unwrap().open).abs();
        let diff_abs_change: f64 = bars.iter().map(|x| (x.close - x.open).abs()).sum();
        
        if diff_abs_change == 0.0 {
            return 0.0;
        }
        
        total_change / diff_abs_change
    }

    /// 笔内部高低点之间的斜率
    pub fn slope(&self) -> f64 {
        let bars = self.raw_bars();
        if bars.len() < 2 {
            return 0.0;
        }
        
        let n = bars.len() as f64;
        let x_sum: f64 = (0..bars.len()).map(|i| i as f64).sum();
        let y_sum: f64 = bars.iter().map(|x| x.close).sum();
        let xy_sum: f64 = bars.iter().enumerate().map(|(i, x)| i as f64 * x.close).sum();
        let x2_sum: f64 = (0..bars.len()).map(|i| (i as f64).powi(2)).sum();
        
        let slope = (n * xy_sum - x_sum * y_sum) / (n * x2_sum - x_sum.powi(2));
        slope
    }

    /// 笔内部价格的加速度
    pub fn acceleration(&self) -> f64 {
        let bars = self.raw_bars();
        if bars.len() < 3 {
            return 0.0;
        }
        
        let n = bars.len() as f64;
        let x_sum: f64 = (0..bars.len()).map(|i| i as f64).sum();
        let x2_sum: f64 = (0..bars.len()).map(|i| (i as f64).powi(2)).sum();
        let x3_sum: f64 = (0..bars.len()).map(|i| (i as f64).powi(3)).sum();
        let x4_sum: f64 = (0..bars.len()).map(|i| (i as f64).powi(4)).sum();
        
        let y_sum: f64 = bars.iter().map(|x| x.close).sum();
        let xy_sum: f64 = bars.iter().enumerate().map(|(i, x)| i as f64 * x.close).sum();
        let x2y_sum: f64 = bars.iter().enumerate().map(|(i, x)| (i as f64).powi(2) * x.close).sum();
        
        let det = n * x2_sum * x4_sum + 2.0 * x_sum * x2_sum * x3_sum - x_sum.powi(2) * x4_sum - n * x3_sum.powi(2) - x2_sum.powi(3);
        
        if det == 0.0 {
            return 0.0;
        }
        
        let a = (n * x2_sum * x2y_sum + x_sum * x3_sum * y_sum + x2_sum * x2_sum * xy_sum - x_sum.powi(2) * x2y_sum - n * x3_sum * xy_sum - x2_sum * x3_sum * y_sum) / det;
        a
    }

    /// 笔的斜边长度
    pub fn hypotenuse(&self) -> f64 {
        let power_price = self.power_price();
        let length = self.raw_bars().len() as f64;
        (power_price.powi(2) + length.powi(2)).sqrt()
    }

    /// 笔的斜边与竖直方向的夹角，角度越大，力度越大
    pub fn angle(&self) -> f64 {
        let power_price = self.power_price();
        let hypotenuse = self.hypotenuse();
        if hypotenuse == 0.0 {
            return 0.0;
        }
        (power_price / hypotenuse).asin() * 180.0 / std::f64::consts::PI
    }

    /// 构成笔的原始K线序列，不包含首尾分型的首根K线
    pub fn raw_bars(&self) -> Vec<&super::bar::RawBar> {
        if self.bars.len() < 2 {
            return Vec::new();
        }
        
        let mut result = Vec::new();
        // 去掉首尾分型的第一根K线
        for bar in &self.bars[1..self.bars.len()-1] {
            result.extend(bar.raw_bars());
        }
        result
    }

    /// 获取缓存值
    pub fn get_cache<T>(&self, key: &str) -> Option<T>
    where
        T: for<'de> Deserialize<'de>,
    {
        self.cache.get(key).and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    /// 设置缓存值
    pub fn set_cache<T>(&mut self, key: &str, value: T)
    where
        T: Serialize,
    {
        if let Ok(v) = serde_json::to_value(value) {
            self.cache.insert(key.to_string(), v);
        }
    }
}

impl std::fmt::Display for BI {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "BI(symbol={}, sdt={}, edt={}, direction={}, high={}, low={})",
            self.symbol,
            self.sdt(),
            self.edt(),
            self.direction,
            self.high(),
            self.low()
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::objects::enums::{Direction, Mark};

    #[test]
    fn test_bi() {
        let dt = Utc::now();
        
        let new_bar1 = NewBar::new(
            "000001.SZ".to_string(),
            1,
            dt,
            crate::objects::enums::Freq::Day,
            10.0,
            11.0,
            12.0,
            9.0,
            1000.0,
            11000.0,
        );

        let new_bar2 = NewBar::new(
            "000001.SZ".to_string(),
            2,
            dt,
            crate::objects::enums::Freq::Day,
            11.0,
            10.0,
            12.0,
            9.0,
            1000.0,
            11000.0,
        );

        let new_bar3 = NewBar::new(
            "000001.SZ".to_string(),
            3,
            dt,
            crate::objects::enums::Freq::Day,
            10.0,
            9.0,
            12.0,
            8.0,
            1000.0,
            11000.0,
        );

        let fx_a = FX::new(
            "000001.SZ".to_string(),
            dt,
            Mark::D,
            12.0,
            8.0,
            9.0,
            vec![new_bar1.clone()],
        );

        let fx_b = FX::new(
            "000001.SZ".to_string(),
            dt,
            Mark::G,
            13.0,
            7.0,
            12.0,
            vec![new_bar3.clone()],
        );

        let bi = BI::new(
            "000001.SZ".to_string(),
            fx_a,
            fx_b,
            vec![],
            Direction::Up,
            vec![new_bar1, new_bar2, new_bar3],
        );

        assert_eq!(bi.direction, Direction::Up);
        assert_eq!(bi.length(), 3);
        assert!(bi.change() > 0.0);
    }
} 